#include "StdAfx.h"
#include "BankMgr.h"


CBankMgr::CBankMgr(u32 size, u32 baseAddr)
{
	for (u32 i=0;i<UNIT_COUNT;i++)
		m_units[i] = ~0UL;
	m_cellSize = size / CELL_COUNT;
	m_baseAddr = baseAddr;
	m_free = CELL_COUNT;
	m_lastAlloc = 0;
}

int CBankMgr::isFreeCell(u32 b)
{
	return (m_units[b/32] & (1UL << (b%32)))>0;
}

void CBankMgr::setFreeCell(u32 b)
{
	m_units[b/32] |= (1UL << (b%32));
}

void CBankMgr::setFullCell(u32 b)
{
	m_units[b/32] &= ~(1UL << (b%32));	
}
	
BOOL CBankMgr::findFreeSpace(u32 p1, u32 p2, u32 size, u32 *cell)
{
	u32 p = p1;

	while (p<p2)
	{
		while (!isFreeCell(p) && (p<p2))
			p++;

		u32 n = 0;
		while (isFreeCell(p+n) && (p<p2) && (n < size))
			n++;

		if (n == size)
		{
			*cell = p;
			return TRUE;
		}
		else
			p += n;
	}
	return FALSE;
}

u32 CBankMgr::toCells(u32 size)
{
	return (size + m_cellSize - 1) / m_cellSize;
}

BOOL CBankMgr::alloc(u32 size, u32 *addr)
{
	//how many cells do we need?
	u32 cnt = toCells(size);

	//do we have enough total space?
	if (cnt > m_free)
		return -1;

	//if someone freed some space since last alloc, go there
	while (isFreeCell(m_lastAlloc) && (m_lastAlloc>0))
		m_lastAlloc--;

	u32 p;
	//now search
	if (!findFreeSpace(m_lastAlloc, CELL_COUNT, cnt, &p))
	{
		if (!findFreeSpace(0, m_lastAlloc, cnt, &p))
			return FALSE;
	}

	m_lastAlloc = p;

	*addr = m_lastAlloc * m_cellSize + m_baseAddr;

	for (u32 i=0;i<cnt;i++)
		setFullCell(m_lastAlloc+i);

	return TRUE;
}

s32 CBankMgr::alloc(u32 size)
{
	u32 addr;

	if (alloc(size, &addr))
		return (s32)addr;

	return -1;
}


void CBankMgr::dealloc(u32 addr, u32 size)
{
	u32 cell = toCells(addr);
	u32 cnt = toCells(size);

	for (u32 i=0;i<cnt;i++)
		setFreeCell(cell + i);

	if (cell < m_lastAlloc)
	{
		m_lastAlloc = cell;
		while (isFreeCell(m_lastAlloc) && (m_lastAlloc>0))
			m_lastAlloc--;

	}
}